//Copyright (c) Microsoft Corporation. All rights reserved.
using System;
using System.IO;
using System.Runtime.InteropServices;

namespace Microsoft.Office.OneNote
{
	/// <summary>
	/// Wraps the OneNote IApplication and CApplication COM interface and maps application
	/// errors to C# exceptions. You would not normally need to use this directly unless you want
	/// to directly call the underlying COM methods. The C# classes encapsulate these mathods for 
	/// you.
	/// </summary>
	public class ApplicationServer
	{
		/// <summary>
		/// Creates a new ApplicationServer object.  A new server should be created
		/// before performing any import, export or navigation operations as construction
		/// will implicitly launch OneNote.exe.  If OneNote is not installed, or cannot 
		/// be found, a <see cref="OneNoteException"/> will be thrown.
		/// </summary>
		public ApplicationServer()
		{
			try
			{
				// CoCreate the application.  This will start the EXE 
				// server if necessary.
				CApplication application = new CApplication();

				// QI it for IApplication:
				server = (IApplication) application;
			}
			catch (COMException e)
			{
				if (0x80040154 == (uint) e.ErrorCode)
				{
					throw new OneNoteException("OneNote is not installed on this machine!");
				}

				throw;
			}
			catch (FileNotFoundException)
			{
				throw new OneNoteException("OneNote.exe cannot be found, please reinstall!");
			}
		}

		#region COM interface wrappers
		public void GetHierarchy(string StartNodeId, HierarchyScope Scope, out string HierarchyXmlOut)
		{
			try
			{
				server.GetHierarchy(StartNodeId, Scope, out HierarchyXmlOut);
			}
			catch (COMException e)
			{
				throw new OneNoteException(e.ErrorCode);
			}
		}

		public void UpdateHierarchy(string ChangesXmlIn)
		{
			try
			{
				server.UpdateHierarchy(ChangesXmlIn);
			}
			catch (COMException e)
			{
				throw new OneNoteException(e.ErrorCode);
			}
		}

		public void OpenHierarchy(string Path, string RelativeToObjectID, out string ObjectID, CreateFileType IfNotExist)
		{
			try
			{
				server.OpenHierarchy(Path, RelativeToObjectID, out ObjectID, IfNotExist);
			}
			catch (COMException e)
			{
				throw new OneNoteException(e.ErrorCode);
			}
		}

		public void OpenHierarchy(string Path, string RelativeToObjectID, out string ObjectID)
		{
			try
			{
				server.OpenHierarchy(Path, RelativeToObjectID, out ObjectID, CreateFileType.None);
			}
			catch (COMException e)
			{
				throw new OneNoteException(e.ErrorCode);
			}
		}

		public void DeleteHierarchy(string ObjectID, DateTime ExpectedLastModified)
		{
			try
			{
				server.DeleteHierarchy(ObjectID, ExpectedLastModified);
			}
			catch (COMException e)
			{
				throw new OneNoteException(e.ErrorCode);
			}
		}

		public void DeleteHierarchy(string ObjectID)
		{
			try
			{
				server.DeleteHierarchy(ObjectID, DateTime.MinValue);
			}
			catch (COMException e)
			{
				throw new OneNoteException(e.ErrorCode);
			}
		}

		public void CreateNewPage(string SectionID, out string PageID, NewPageStyle newPageStyle)
		{
			try
			{
				server.CreateNewPage(SectionID, out PageID, newPageStyle);
			}
			catch (COMException e)
			{
				throw new OneNoteException(e.ErrorCode);
			}
		}

		public void CreateNewPage(string SectionID, out string PageID)
		{
			try
			{
				server.CreateNewPage(SectionID, out PageID, NewPageStyle.npsDefault);
			}
			catch (COMException e)
			{
				throw new OneNoteException(e.ErrorCode);
			}
		}

		public void CloseNotebook(string NotebookID)
		{
			try
			{
				server.CloseNotebook(NotebookID);
			}
			catch (COMException e)
			{
				throw new OneNoteException(e.ErrorCode);
			}
		}

		// Page content level operations
		public void GetPageContent(string PageID, out string PageXmlOut, PageInfo pageInfoToExport)
		{
			try
			{
				server.GetPageContent(PageID, out PageXmlOut, pageInfoToExport);
			}
			catch (COMException e)
			{
				throw new OneNoteException(e.ErrorCode);
			}
		}

		public void GetPageContent(string PageID, out string PageXmlOut)
		{
			try
			{
				server.GetPageContent(PageID, out PageXmlOut, PageInfo.Basic);
			}
			catch (COMException e)
			{
				throw new OneNoteException(e.ErrorCode);
			}
		}

		public void UpdatePageContent(string PageChangesXmlIn, DateTime ExpectedLastModified)
		{
			try
			{
				server.UpdatePageContent(PageChangesXmlIn, ExpectedLastModified);
			}
			catch (COMException e)
			{
				throw new OneNoteException(e.ErrorCode);
			}
		}

		public void UpdatePageContent(string PageChangesXmlIn)
		{
			try
			{
				server.UpdatePageContent(PageChangesXmlIn, DateTime.MinValue);
			}
			catch (COMException e)
			{
				throw new OneNoteException(e.ErrorCode);
			}
		}

		public void GetBinaryPageContent(string PageID, string CallbackID, out string BinaryObjectOut)
		{
			try
			{
				server.GetBinaryPageContent(PageID, CallbackID, out BinaryObjectOut);
			}
			catch (COMException e)
			{
				throw new OneNoteException(e.ErrorCode);
			}
		}

		public void DeletePageContent(string PageID, string ObjectID, DateTime ExpectedLastModified)
		{
			try
			{
				server.DeletePageContent(PageID, ObjectID, ExpectedLastModified);
			}
			catch (COMException e)
			{
				throw new OneNoteException(e.ErrorCode);
			}
		}

		public void DeletePageContent(string PageID, string ObjectID)
		{
			try
			{
				server.DeletePageContent(PageID, ObjectID, DateTime.MinValue);
			}
			catch (COMException e)
			{
				throw new OneNoteException(e.ErrorCode);
			}
		}

		// Actions
		public void NavigateTo(string ObjectID, bool NewWindow)
		{
			try
			{
				server.NavigateTo(ObjectID, System.String.Empty, NewWindow);
			}
			catch (COMException e)
			{
				throw new OneNoteException(e.ErrorCode);
			}
		}

		public void NavigateTo(string ObjectID)
		{
			try
			{
				server.NavigateTo(ObjectID, System.String.Empty, false);
			}
			catch (COMException e)
			{
				throw new OneNoteException(e.ErrorCode);
			}
		}

		public void Publish(string HierarchyId, string TargetFilePath, PublishFormat publishFormat, string CLSIDofExporter)
		{
			try
			{
				server.Publish(HierarchyId, TargetFilePath, publishFormat, CLSIDofExporter);
			}
			catch (COMException e)
			{
				throw new OneNoteException(e.ErrorCode);
			}
		}

		public void Publish(string HierarchyId, string TargetFilePath, PublishFormat publishFormat)
		{
			try
			{
				server.Publish(HierarchyId, TargetFilePath, publishFormat, "0");
			}
			catch (COMException e)
			{
				throw new OneNoteException(e.ErrorCode);
			}
		}

		public void Publish(string HierarchyId, string TargetFilePath)
		{
			try
			{
				server.Publish(HierarchyId, TargetFilePath, PublishFormat.OneNote, "0");
			}
			catch (COMException e)
			{
				throw new OneNoteException(e.ErrorCode);
			}
		}

		public void OpenPackage(string bstrPathPackage, string bstrPathDest, out string pbstrPathOut)
		{
			try
			{
				server.OpenPackage(bstrPathPackage, bstrPathDest, out pbstrPathOut);
			}
			catch (COMException e)
			{
				throw new OneNoteException(e.ErrorCode);
			}
		}

		public void GetHyperlinkToObject(string HierarchyId, string TargetFilePath, out string Hyperlink)
		{
			try
			{
				server.GetHyperlinkToObject(HierarchyId, TargetFilePath, out Hyperlink);
			}
			catch (COMException e)
			{
				throw new OneNoteException(e.ErrorCode);
			}
		}

		public void FindPages(string StartNodeID, string SearchString, out string HierarchyXml, bool Display)
		{
			try
			{
				server.FindPages(StartNodeID, SearchString, out HierarchyXml, false, Display);
			}
			catch (COMException e)
			{
				throw new OneNoteException(e.ErrorCode);
			}
		}

		// TODO: Add support for FindMeta

		public void GetSpecialLocation(SpecialLocation specialLocation, out string specialLocationPath)
		{
			try
			{
				server.GetSpecialLocation(specialLocation, out specialLocationPath);
			}
			catch (COMException e)
			{
				throw new OneNoteException(e.ErrorCode);
			}
		}
		#endregion

		public static string XmlNamespace = "http://schemas.microsoft.com/office/onenote/2007/onenote";

		private IApplication server;
	}

	/// <summary>
	/// Specifies the scope of a GetHierarchy call.
	/// </summary>
	[Flags]
	public enum HierarchyScope : int
	{
		/// <summary>The specified hierarchy node</summary>
		Self = 0,

		/// <summary>The specified hierarchy node and its children</summary>
		Children = 1,

		/// <summary>All of the hierarchy from the specified node until the notebook children (inclusive)</summary>
		Notebooks = 2,

		/// <summary>All of the hierarchy from the specified node until the section children (inclusive)</summary>
		Sections = 3,

		/// <summary>All of the hierarchy from the specified node until the page children (inclusive)</summary>
		Pages = 4,
	}

	public enum CreateFileType : int
	{
		None = 0,
		Notebook = 1,
		Folder = 2,
		Section = 3,
	}

	public enum PageInfo : int
	{
		// Basic types
		Basic = 0,
		BinaryData = 1,
		Selection = 2,

		// Unions
		BinaryDataSelection = BinaryData | Selection,
		All = Basic | BinaryData | Selection,
	}

	public enum PublishFormat : int
	{
		OneNote = 0, // .one
		OneNoteArchive = 1, // .onea
		MHTML = 2, // .mht
		PDF = 3, // .pdf
		XPS = 4, // .xps
		Word = 5, // .doc
	}

	public enum SpecialLocation : int
	{
		slBackupFolder = 0,
		slUnfiledNotesSeciton = 1,
		slDefaultNotebookFolder = 2,
	}

	public enum NewPageStyle : int
	{
		npsDefault = 0,
		npsBlankPageWithTitle = 1,
		npsBlankPageNoTitle = 2,
	}

	/// <summary>
	/// The IApplication is a COM interface which can be used to create
	/// OneNote hierarchy such as folders, sections and pages and import/export
	/// content into this hierarchy (marshalled in an XML format).  
	/// </summary>
	[Guid("2DA16203-3F58-404f-839D-E4CDE7DD0DED"), InterfaceType(ComInterfaceType.InterfaceIsDual)]
	interface IApplication
	{
		// Hierarchy Level Operations
		void GetHierarchy(
			[In, MarshalAs(UnmanagedType.BStr)] string StartNodeID,
			[In] HierarchyScope hsScope,
			[Out, MarshalAs(UnmanagedType.BStr)] out string HierarchyXmlOut);

		void UpdateHierarchy(
			[In, MarshalAs(UnmanagedType.BStr)] string bstrChangesXmlIn);

		void OpenHierarchy(
			[In, MarshalAs(UnmanagedType.BStr)] string bstrPath,
			[In, MarshalAs(UnmanagedType.BStr)] string bstrRelativeToObjectID,
			[Out, MarshalAs(UnmanagedType.BStr)] out string pbstrObjectID,
			[In] CreateFileType cftIfNotExist);

		void DeleteHierarchy(
			[In, MarshalAs(UnmanagedType.BStr)] string bstrObjectID,
			[In] DateTime dateExpectedLastModified);

		void CreateNewPage(
			[In, MarshalAs(UnmanagedType.BStr)] string bstrSectionID,
			[Out, MarshalAs(UnmanagedType.BStr)] out string pbstrPageID,
			[In] NewPageStyle npsNewPageStyle);

		void CloseNotebook(
			[In, MarshalAs(UnmanagedType.BStr)] string bstrNotebookID);

		void GetHierarchyParent(
			[In, MarshalAs(UnmanagedType.BStr)] string bstrObjectID,
			[Out, MarshalAs(UnmanagedType.BStr)] out string pbstrParentID);

		// Page content level operations
		void GetPageContent(
			[In, MarshalAs(UnmanagedType.BStr)] string bstrPageID,
			[Out, MarshalAs(UnmanagedType.BStr)] out string pbstrPageXmlOut,
			[In] PageInfo pageInfoToExport);

		void UpdatePageContent(
			[In, MarshalAs(UnmanagedType.BStr)] string bstrPageChangesXmlIn,
			[In] DateTime dateExpectedLastModified);

		void GetBinaryPageContent(
			[In, MarshalAs(UnmanagedType.BStr)] string bstrPageID,
			[In, MarshalAs(UnmanagedType.BStr)] string bstrCallbackID,
			[Out, MarshalAs(UnmanagedType.BStr)] out string pbstrBinaryObjectB64Out);

		void DeletePageContent(
			[In, MarshalAs(UnmanagedType.BStr)] string bstrPageID,
			[In, MarshalAs(UnmanagedType.BStr)] string bstrObjectID,
			[In] DateTime dateExpectedLastModified);

		// Navigation
		void NavigateTo(
			[In, MarshalAs(UnmanagedType.BStr)] string bstrHierarchyObjectID,
			[In, MarshalAs(UnmanagedType.BStr)] string bstrObjectID,
			[In, MarshalAs(UnmanagedType.VariantBool)] bool fNewWindow);

		// Publish
		void Publish(
			[In, MarshalAs(UnmanagedType.BStr)] string bstrHierarchyID,
			[In, MarshalAs(UnmanagedType.BStr)] string bstrTargetFilePath,
			[In] PublishFormat pfPublishFormat,
			[In, MarshalAs(UnmanagedType.BStr)] string bstrCLSIDofExporter);

		void OpenPackage(
			[In, MarshalAs(UnmanagedType.BStr)] string bstrPathPackage,
			[In, MarshalAs(UnmanagedType.BStr)] string bstrPathDest,
			[Out, MarshalAs(UnmanagedType.BStr)] out string pbstrPathOut);

		// Hyperlinks
		void GetHyperlinkToObject(
			[In, MarshalAs(UnmanagedType.BStr)] string bstrHierarchyID,
			[In, MarshalAs(UnmanagedType.BStr)] string bstrPageContentObjectID,
			[Out, MarshalAs(UnmanagedType.BStr)] out string pbstrHyperlinkOut);

		// Search
		void FindPages(
			[In, MarshalAs(UnmanagedType.BStr)] string bstrStartNodeID,
			[In, MarshalAs(UnmanagedType.BStr)] string bstrSearchString,
			[Out, MarshalAs(UnmanagedType.BStr)] out string pbstrHierarchyXmlOut,
			[In, MarshalAs(UnmanagedType.VariantBool)] bool fIncludeUnindexedPages,
			[In, MarshalAs(UnmanagedType.VariantBool)] bool fDisplay);

		void FindMeta(
			[In, MarshalAs(UnmanagedType.BStr)] string bstrStartNodeID,
			[In, MarshalAs(UnmanagedType.BStr)] string bstrSearchStringName,
			[Out, MarshalAs(UnmanagedType.BStr)] out string pbstrHierarchyXmlOut,
			[In, MarshalAs(UnmanagedType.VariantBool)] bool fIncludeUnindexedPages);

		void GetSpecialLocation(
			[In] SpecialLocation slToGet,
			[Out, MarshalAs(UnmanagedType.BStr)] out string pbstrSpecialLocationPath);
	}

	/// <summary>
	/// The COM component that can be instantiated and subsequently cast to an
	/// IApplication.
	/// </summary>
	[ComImport, Guid("0039FFEC-A022-4232-8274-6B34787BFC27")]
	class CApplication
	{
	}
}